package org.hepeng.fabric.gateway.event;

import org.hyperledger.fabric.client.ChaincodeEvent;
import org.hyperledger.fabric.client.CloseableIterator;
import org.hyperledger.fabric.client.Network;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Set;

/**
 * @author he peng
 * @date 2022/3/30
 */
public class EventPoller implements Runnable {

    private static final Logger LOG = LoggerFactory.getLogger(EventPoller.class);

    private final String chaincodeName;

    public EventPoller(String chaincodeName) {
        this.chaincodeName = chaincodeName;
    }

    @Override
    public void run() {

        try {
            while (true) {
                Network network = NetworkCache.get(chaincodeName);
                if (Objects.nonNull(network)) {
                    String channel = network.getName();
                    CloseableIterator<ChaincodeEvent> events = network.getChaincodeEvents(chaincodeName);
                    while (events.hasNext()) {
                        ChaincodeEvent event = events.next();
                        invokeEventListener(event , channel);

                    }
                }
            }
        } catch (Exception e) {
            LOG.error(String.format("get chaincode events exception , chaincode[%s]" , chaincodeName) , e);
        }
    }

    private void invokeEventListener(ChaincodeEvent event , String channel) {
        CCEventDefinition definition = new CCEventDefinition(chaincodeName, channel, event.getEventName());
        Set<CCEventListenerMethod> eventListenerMethods = CCEventListenerRegistry.getEventListenerMethods(definition);

        if (Objects.isNull(eventListenerMethods)) {
            return;
        }

        try {
            for (CCEventListenerMethod listenerMethod : eventListenerMethods) {
                Set<Method> methods = listenerMethod.getListenerMethods();
                if (Objects.nonNull(methods)) {
                    for (Method method : methods) {
                        method.setAccessible(true);
                        method.invoke(listenerMethod.getTarget() , event);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
